Skip to content

feat(selfhost): cache stable GitHub GraphQL reads#1880

Merged
JSONbored merged 9 commits into
JSONbored:mainfrom
andriypolanski:feat/selfhost-graphql-response-cache
Jul 1, 2026
Merged

feat(selfhost): cache stable GitHub GraphQL reads#1880
JSONbored merged 9 commits into
JSONbored:mainfrom
andriypolanski:feat/selfhost-graphql-response-cache

Conversation

@andriypolanski

@andriypolanski andriypolanski commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Summary

#1842 added an auth-aware shared response cache for stable GitHub REST GETs used by Octokit and raw backfill reads. Self-host backfill and hydration still POST to https://api.github.com/graphql on every segment sweep with no shared cache, even for idempotent repository totals and contributor-activity queries that repeat across webhook bursts, re-gate sweeps, and concurrent segment workers.

This extends the same cache boundary to allowlisted stable GraphQL operations — reducing duplicate quota burn without caching mutable PR/issue/review-thread subresources that gate scoring and review decisions.

What this adds

  • Centralized GraphQL response caching alongside the existing REST cache in src/github/client.ts / src/selfhost/redis-response-cache.ts, keyed by auth hash + normalized query signature (operation name + stable variables).
  • Allowlist stable operations such as GittensoryRepoTotals and GittensoryContributorActivity; bypass live PR detail, review-thread, review-decision, open-issue supplement, and open-PR supplement queries.
  • Single-flight cold misses, per-operation TTL overrides, replay headers, Prometheus counters (gittensory_github_graphql_cache_total), and fail-open behavior on Redis errors — mirroring the REST cache semantics from feat(selfhost): cache stable GitHub GET reads #1842.
  • Route githubGraphQl in src/github/backfill.ts through the shared cache path; skip rate-limit observation writes for cache replays.

Purely operational — no scoring, review, gate, or MCP behavior change.

Files touched (estimated)

File Change
src/github/client.ts Export GraphQL cache types/helpers; shared replay header + metric names
src/github/graphql-cache.ts New: allowlist, cache key normalization, fetch-with-cache wrapper
src/github/backfill.ts Route githubGraphQl through shared cache; preserve existing error/rate-limit paths
src/selfhost/redis-response-cache.ts Optional separate key prefix for GraphQL payloads (or reuse gh:resp: with gql: namespace)
src/server.ts Wire GraphQL cache when REST cache is configured
test/unit/github-graphql-cache.test.ts Allowlist, auth isolation, single-flight, bypass mutable ops, malformed cache, replay metrics
test/unit/backfill.test.ts Totals snapshot reuse / cache hit regression
Self-host Grafana dashboard + docs Cache panels and env knobs (GITHUB_GRAPHQL_CACHE_TTL_SECONDS)

Validation

  • git diff --check
  • npm run actionlint
  • npm run typecheck
  • npm run test:coverage — aim for 100% statements/branches on new/changed lines in src/github/graphql-cache.ts and cache wiring
  • npm run test:workers
  • npm run build:mcp
  • npm run test:mcp-pack
  • npm run ui:openapi:check
  • npm run ui:lint
  • npm run ui:typecheck
  • npm run ui:build
  • npm audit --audit-level=moderate

Targeted test run:

npx vitest run test/unit/github-graphql-cache.test.ts test/unit/github-response-cache.test.ts --coverage --coverage.include='src/github/graphql-cache.ts' --coverage.include='src/github/client.ts'

Notes

Analogues to imitate end-to-end: REST cache in src/github/client.ts, createRedisResponseCache in src/selfhost/redis-response-cache.ts, and backfill rate-limit deferral in src/github/backfill.ts shouldWaitForGitHubRateLimit.

@dosubot dosubot Bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Jun 30, 2026
@gittensory-orb

gittensory-orb Bot commented Jun 30, 2026

Copy link
Copy Markdown

Warning

🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨

⏸️ Gittensory review result - manual review recommended

Review updated: 2026-07-01 16:15:18 UTC

6 files · 1 AI reviewer · no blockers · readiness 48/100 · CI green · clean

⏸️ Suggested Action - Manual Review

  • Touches a guarded path — held for manual review

Review summary
The change cleanly routes allowlisted backfill GraphQL reads through the existing shared response-cache boundary, preserves live rate-limit recording, and avoids caching HTTP 200 GraphQL error envelopes. The core cache path is covered for hits, misses, token isolation, cache failures, and replay handling, so the visible diff is safe enough to proceed. The most notable remaining concern is maintainability: the allowlist and TTL behavior are more name-based/general than the PR description implies.

Nits — 7 non-blocking
  • nit: src/github/graphql-cache.ts:24 allows caching solely by GraphQL operation name, so future mutable fields added under an allowlisted operation name would be cached unless reviewers remember this convention.
  • nit: src/github/graphql-cache.ts:53 accepts a cache class but ignores it, which makes the advertised per-operation TTL override behavior misleading.
  • nit: src/github/graphql-cache.ts:159 only replays coalesced callers when the leader produced a cacheable response, so concurrent non-cacheable cold misses still serialize into duplicate upstream requests.
  • nit: test/unit/github-graphql-cache.test.ts:404 duplicates the rate-limit observation boundary logic instead of exercising the production backfill helper, which makes that test easier to drift from the real path.
  • src/github/graphql-cache.ts:24 should either document the operation-name-only allowlist as the contract or add a lightweight structural check for the fields expected in each cache class.
  • Readiness score is below the configured threshold — Use the readiness panel as advisory maintainer context; the score does not block this PR.
  • Touches a guarded path — held for manual review — A maintainer must review and merge this change.
Signal Result Evidence
Code review ✅ No blockers 1 reviewer
Linked issue ⚠️ Missing No linked issue or no-issue rationale found.
Related work ⚠️ 3 scoped overlaps Top overlaps are listed below; lower-confidence bulk is hidden.
Change scope ❌ 8/20 High review scope from cached public metadata (size label size:L; no linked issue context).
Validation posture ❌ 5/25 Preflight is holding this PR: the review lane is unavailable, so it is not ready for automated review.
Contributor workload ✅ 10/10 Author activity: 100 registered-repo PR(s), 66 merged, 5 issue(s).
Contributor context ✅ Confirmed Gittensor contributor andriypolanski; Gittensor profile; 100 PR(s), 5 issue(s).
Gate result ⚠️ Not blocking Advisory; not blocking this PR.
Review context
Contributor next steps
  • Explain no-issue PR.
  • Review top overlaps.
  • Add a concise scope and risk note.
  • Await review-lane availability.
  • Triage stale or unlinked PRs.
  • Refresh registry data or choose a registered active repo.
  • Link the issue being solved, or explicitly explain why this is a no-issue PR.
  • Check active issues and PRs before submitting.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.

🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed


💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →.

Checked by Gittensory, a quiet PR intelligence layer for OSS maintainers.

  • Re-run Gittensory review

@gittensory-orb gittensory-orb Bot added gittensor Gittensor contributor context gittensor:feature Gittensor-scored feature linked to a feature issue — scores a 1.25x multiplier. labels Jun 30, 2026

@JSONbored JSONbored left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This touches an important area, so i'm holding this temporarily while I confirm whether or not it conflicts with the direction i'm currently taking the cache.

Leave the PR as-is and I'll review it after I'm done implementing outstanding cache changes, please.

If for whatever reason I decide not to use this direction, I'll allow a one-time rewrite of this PR to focus on something else.

Thanks for working on this.

Comment thread src/github/graphql-cache.ts Outdated
@superagent-security superagent-security Bot added the pr:flagged PR flagged for review by security analysis. label Jul 1, 2026
@superagent-security superagent-security Bot removed the pr:flagged PR flagged for review by security analysis. label Jul 1, 2026
@codecov

codecov Bot commented Jul 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.71%. Comparing base (a7fbc49) to head (d3cb63a).
⚠️ Report is 15 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1880      +/-   ##
==========================================
+ Coverage   95.69%   95.71%   +0.01%     
==========================================
  Files         222      223       +1     
  Lines       24584    24663      +79     
  Branches     8923     8942      +19     
==========================================
+ Hits        23526    23605      +79     
  Misses        433      433              
  Partials      625      625              
Files with missing lines Coverage Δ
src/github/app.ts 98.00% <ø> (ø)
src/github/backfill.ts 96.12% <100.00%> (ø)
src/github/client.ts 100.00% <100.00%> (ø)
src/github/graphql-cache.ts 100.00% <100.00%> (ø)
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@andriypolanski andriypolanski force-pushed the feat/selfhost-graphql-response-cache branch from cb2a22d to 2d511bc Compare July 1, 2026 13:34
@JSONbored JSONbored merged commit b3a61bc into JSONbored:main Jul 1, 2026
9 checks passed
@github-project-automation github-project-automation Bot moved this from Todo to Done in gittensory - v1 roadmap Jul 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:feature Gittensor-scored feature linked to a feature issue — scores a 1.25x multiplier. gittensor Gittensor contributor context size:L This PR changes 100-499 lines, ignoring generated files.

Projects

No open projects
Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants